package com.ruoyi.framework.util;

import sun.security.jca.JCAUtil;

import javax.crypto.Cipher;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class RsaUtils {

    private static final int DEFAULT_KEY_SIZE = 2048;
    private static final String ALGORITHM = "RSA";

    /**
     * 根据密文，生存RSA公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param password           生成密钥的密码
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String password, int keySize) throws Exception {
        // 创建KeyPairGenerator对象，指定算法为RSA
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        if (password == null) {
            // 初始化KeyPairGenerator对象，设置密钥长度为2048位
            keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), JCAUtil.getSecureRandom());
        } else {
            SecureRandom secureRandom = new SecureRandom(password.getBytes(StandardCharsets.UTF_8));
            // 初始化KeyPairGenerator对象，设置密钥长度为2048位
            keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
        }

        // 生成KeyPair对象，即公钥和私钥
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写到文件
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
        writeFile(publicKeyFilename, publicKeyBytes);

        // 获取私钥并写到文件
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    /**
     * 从文件中读取公钥为PublicKey对象
     *
     * @param filename 公钥保存路径，相对于classpath
     * @return 公钥对象
     * @throws Exception 读取公钥抛出的异常类型
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readBytesFromFile(filename);
        byte[] decodeBytes = Base64.getDecoder().decode(bytes);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(decodeBytes);
        KeyFactory factory = KeyFactory.getInstance(ALGORITHM);
        return factory.generatePublic(spec);
    }

    /**
     * 从文件中读取私钥为PrivateKey对象
     *
     * @param filename 私钥保存路径，相对于classpath
     * @return 私钥对象
     * @throws Exception 读取私钥抛出的异常类型
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readBytesFromFile(filename);
        byte[] decodeBytes = Base64.getDecoder().decode(bytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decodeBytes);
        KeyFactory factory = KeyFactory.getInstance(ALGORITHM);
        return factory.generatePrivate(spec);
    }

    /**
     * RSA公钥加密
     *
     * @param plainText     明文
     * @param publicKeyPath 公钥文件路径
     * @return 密文
     * @throws Exception 加密过程中的异常信息
     */
    public static String encrypt(String plainText, String publicKeyPath) throws Exception {
        // base64编码的公钥
        PublicKey publicKey = getPublicKey(publicKeyPath);
        // RSA加密
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] cipherBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(cipherBytes);
    }

    /**
     * RSA公钥加密
     *
     * @param plainText     明文
     * @return 密文
     * @throws Exception 加密过程中的异常信息
     */
    public static String encrypt(String plainText, PublicKey publicKey) throws Exception {
        // base64编码的公钥
//        PublicKey publicKey = getPublicKey(publicKeyPath);
        // RSA加密
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] cipherBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(cipherBytes);
    }

    /**
     * RSA私钥解密
     *
     * @param cipherText     密文
     * @param privateKeyPath 私钥文件路径
     * @return 明文
     */
    public static String decrypt(String cipherText, String privateKeyPath) throws Exception {
        // 64位解码加密后的字符串
        byte[] inputBytes;
        inputBytes = Base64.getDecoder().decode(cipherText.getBytes(StandardCharsets.UTF_8));
        PrivateKey privateKey = getPrivateKey(privateKeyPath);
        // RSA解密
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(cipher.doFinal(inputBytes));
    }

    /**
     * RSA私钥解密
     *
     * @return 明文
     */
    public static String decrypt(String cipherText, PrivateKey privateKey) throws Exception {
//        // 64位解码加密后的字符串
        byte[] inputBytes;
        inputBytes = Base64.getDecoder().decode(cipherText.getBytes(StandardCharsets.UTF_8));
//        PrivateKey privateKey = getPrivateKey(privateKeyPath);
        // RSA解密
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(cipher.doFinal(inputBytes));
    }

    public static void generateKey(String pubKeyFileName, String priKeyFileName) throws Exception {
        generateKey(pubKeyFileName, priKeyFileName, null, DEFAULT_KEY_SIZE);
    }

    private static byte[] readBytesFromFile(String fileName) throws Exception {
        return Files.readAllBytes(new File(fileName).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);
        if (dest.exists()) {
            Files.write(dest.toPath(), bytes);
            return;
        }
        boolean created = dest.createNewFile();
        if (!created) {
            System.out.println("文件写入失败，文件地址："+destPath);
        }
        Files.write(dest.toPath(), bytes);
    }

    public void getPath(){
        String path = this.getClass().getClassLoader().getResource("/ssh").getPath();
    }

//    public static void main(String[] args) throws Exception {
//
//        RsaUtils.generateKey("E:/software/workspace/ry-activiti-vue/ruoyi-framework/src/main/resources/ssh/id_rsa.pub", "E:/software/workspace/ry-activiti-vue/ruoyi-framework/src/main/resources/ssh/id_rsa");
//    }

    public static void main(String[] args) throws Exception {
        String publicKeyPath = "E:\\software\\workspace\\ry-activiti-vue\\ruoyi-admin\\src\\main\\resources\\ssh\\id_rsa.pub";
        String privateKeyPath = "E:\\software\\workspace\\ry-activiti-vue\\ruoyi-admin\\src\\main\\resources\\ssh\\id_rsa";
        System.out.printf("公钥文件路径:%s\n", publicKeyPath);
        System.out.printf("私钥文件路径:%s\n\n", privateKeyPath);

        String a = "admin";

        String acipherText = encrypt(a, publicKeyPath);
        System.out.printf("账号加密前明文是%s\n", a);
        System.out.printf("账号加密后密文是%s\n\n", acipherText);

        String p = "admin123";
        String pcipherText = encrypt(p, publicKeyPath);
        System.out.printf("密码加密前明文是%s\n", p);
        System.out.printf("密码加密后密文是%s\n\n", pcipherText);
//        String plainTextRecovery = decrypt(cipherText, privateKeyPath);
//        System.out.printf("解密前密文是%s\n", cipherText);
//        System.out.printf("解密后明文是%s\n", plainTextRecovery);
//
//        System.out.printf("密码前后一致性:%s\n\n", plainText.equals(plainTextRecovery));
    }


}
